
#include "DataBase.h"

/*
 * File:   ServerClientProtocol.cpp
 * Author: wilq
 *
 * Created on June 13, 2012, 7:41 PM
 */
#include "ServerClientProtocol.h"
#include "ConnectionManager.h"

#define RAISE_EXCEPTION(type, msg) SendExceptionResponse((CSNetworkLib::type), (msg), (request)); return

void ServerClientProtocol::OnUserGetFilesInDir(const NetworkPacket &request) {
    FUNCTION_TRACK
    uint32_t id = 0;
    NetworkPacket response(request);

    if (request.Get_U32(1, id)) {
        //TODO: Throw
    }

    DBUG("OnUserGetFilesInDir %d", id);

    vector<File> fileList = g_db->FileList(id);
    response.Push_U32(fileList.size());
    for (int32_t i = 0; i < fileList.size(); i++) {
        _Push_File(response, fileList[i]);
    }

    this->SendPacket(response);
}

void ServerClientProtocol::OnUserGetFoldersInDir(const NetworkPacket &request) {
    FUNCTION_TRACK
    uint32_t id = 0;
    NetworkPacket response(request);

    if (request.Get_U32(1, id)) {
        //TODO: Throw
    }

    DBUG("OnUserGetFoldersInDir %d", id);

    vector<Folder> folderList = g_db->FolderList(id);
    response.Push_U32(folderList.size());
    for (int32_t i = 0; i < folderList.size(); i++) {
        _Push_Folder(response, folderList[i]);
    }

    this->SendPacket(response);
}

void ServerClientProtocol::OnUserAuthorization(const NetworkPacket &request) {
    FUNCTION_TRACK
    DBUG("ClientAuthData received");
    string userName = _Get_String(request, 0);
    string password = _Get_String(request, 1);
    DBUG("Database processing start");
    this->connectedUserId = g_db->UserAuth(userName, password);

    NetworkPacket response(request, PACKET_CLIENT_AutorizationResp);
    if (this->connectedUserId == 0) {
        //TODO: user not authorizated
        DBUG("User %s not authorizated", userName.c_str());
        response.Push_S32(0);
    } else {
        response.Push_S32(this->connectedUserId);
        DBUG("User %s authorizated", userName.c_str());
        _AddHandlers();
        ConnectionManager::getInstance().PutClient(this->connectedUserId, this);
        g_db->UserUpdateLastAccessDate(connectedUserId);
    }
    DBUG("Send response");
    this->SendPacket(response);
    DBUG("Response send");
}

void ServerClientProtocol::OnUserChangePassword(const NetworkPacket &request) {
    FUNCTION_TRACK
    string userName = _Get_String(request, 0);
    string password = _Get_String(request, 1);
    NetworkPacket response(request);

    response.Push_S32(g_db->UserChangePassword(userName, password));

    this->SendPacket(response);
}

void ServerClientProtocol::_AddHandlers() {
    FUNCTION_TRACK
    RemoveHandler((uint16_t) CSNetworkLib::PACKET_CLIENT_AutorizationReq);

    AddHandler< ServerClientProtocol > ((uint32_t) CSNetworkLib::PACKET_CLIENT_ShareReq, & ServerClientProtocol::OnShare);
    AddHandler< ServerClientProtocol > ((uint32_t) CSNetworkLib::PACKET_CLIENT_UnShareReq, & ServerClientProtocol::OnUnShare);
    AddHandler< ServerClientProtocol > ((uint32_t) CSNetworkLib::PACKET_CLIENT_FileExistsReq, & ServerClientProtocol::OnFileExists);
    AddHandler< ServerClientProtocol > ((uint32_t) CSNetworkLib::PACKET_CLIENT_FileOpenReq, & ServerClientProtocol::OnFileOpen);
    AddHandler< ServerClientProtocol > ((uint32_t) CSNetworkLib::PACKET_CLIENT_FileCloseReq, & ServerClientProtocol::OnFileClose);
    AddHandler< ServerClientProtocol > ((uint32_t) CSNetworkLib::PACKET_CLIENT_FileInfoReq, & ServerClientProtocol::OnFileInfo);
    AddHandler< ServerClientProtocol > ((uint32_t) CSNetworkLib::PACKET_CLIENT_FileEofReq, & ServerClientProtocol::OnFileEof);
    AddHandler< ServerClientProtocol > ((uint32_t) CSNetworkLib::PACKET_CLIENT_FileSeekReq, & ServerClientProtocol::OnFileSeek);
    AddHandler< ServerClientProtocol > ((uint32_t) CSNetworkLib::PACKET_CLIENT_FileReadReq, & ServerClientProtocol::OnFileRead);
    AddHandler< ServerClientProtocol > ((uint32_t) CSNetworkLib::PACKET_CLIENT_FileWriteReq, & ServerClientProtocol::OnFileWrite);
    AddHandler< ServerClientProtocol > ((int32_t) CSNetworkLib::PACKET_CLIENT_FileDeleteReq, & ServerClientProtocol::OnFileDelete);

    AddHandler<ServerClientProtocol > ((int32_t) CSNetworkLib::PACKET_UserGetFilesInDir, & ServerClientProtocol::OnUserGetFilesInDir);
    AddHandler<ServerClientProtocol > ((int32_t) CSNetworkLib::PACKET_UserGetFoldersInDir, & ServerClientProtocol::OnUserGetFoldersInDir);
    AddHandler<ServerClientProtocol > ((int32_t) CSNetworkLib::PACKET_UserCreateFolder, & ServerClientProtocol::OnUserMakeDirectory);
    AddHandler<ServerClientProtocol > ((int32_t) CSNetworkLib::PACKET_UserDeleteFolder, & ServerClientProtocol::OnUserDeleteDirectory);


    AddHandler<ServerClientProtocol > ((int32_t) CSNetworkLib::PACKET_UserRenameFile, & ServerClientProtocol::OnUserRenameFile);
    AddHandler<ServerClientProtocol > ((int32_t) CSNetworkLib::PACKET_UserRenameFolder, & ServerClientProtocol::OnUserRenameDirectory);
    //TODO: add rest
    AddHandler<ServerClientProtocol > ((int32_t) CSNetworkLib::PACKET_UserAddUser, & ServerClientProtocol::OnUserAddUser);
    AddHandler<ServerClientProtocol > ((int32_t) CSNetworkLib::PACKET_UserChangePassword, & ServerClientProtocol::OnUserChangePassword);
    AddHandler<ServerClientProtocol > ((int32_t) CSNetworkLib::PACKET_UserRemoveUser, & ServerClientProtocol::OnUserRemoveUser);

    AddHandler<ServerClientProtocol > ((int32_t) CSNetworkLib::PACKET_UserAddGroup, & ServerClientProtocol::OnUserAddGroup);
    AddHandler<ServerClientProtocol > ((int32_t) CSNetworkLib::PACKET_UserRemoveGroup, & ServerClientProtocol::OnUserRemoveGroup);

    AddHandler<ServerClientProtocol > ((int32_t) CSNetworkLib::PACKET_UserAddStorage, & ServerClientProtocol::OnUserAddStorage);
    AddHandler<ServerClientProtocol > ((int32_t) CSNetworkLib::PACKET_UserRemoveStorage, & ServerClientProtocol::OnUserRemoveStorage);
    AddHandler<ServerClientProtocol > ((int32_t) CSNetworkLib::PACKET_UserChangeStorageKey, & ServerClientProtocol::OnUserChangeStorageKey);

    AddHandler<ServerClientProtocol > ((int32_t) CSNetworkLib::PACKET_UserGetUsersList, & ServerClientProtocol::OnUserGetUsersList);
    AddHandler<ServerClientProtocol > ((int32_t) CSNetworkLib::PACKET_UserGetGroupsList, & ServerClientProtocol::OnUserGetGroupsList);
    AddHandler<ServerClientProtocol > ((int32_t) CSNetworkLib::PACKET_UserGetStoragesList, & ServerClientProtocol::OnUserGetStoragesList);
}

void ServerClientProtocol::OnUserMakeDirectory(const NetworkPacket &request) {
    FUNCTION_TRACK
    uint32_t parentFolderId = 0;
    NetworkPacket response(request);
    int32_t id;

    request.Get_U32(0, parentFolderId);
    string dirName = _Get_String(request, 1);

    DBUG("Adding directory %s parent id %d", dirName.c_str(), parentFolderId);

    if (parentFolderId != 0) {
        Folder folder = g_db->FolderGet(parentFolderId);

        ServerStorageProtocol* protocol = ConnectionManager::getInstance().getStorageConnection(folder.storageId);
        protocol->FolderCreate(folder.link, dirName);

        //TODO: implement
        //string link = protocol->createDirectory(dirName, folder);
        string link;
        id = g_db->FolderCreate(dirName, folder.id, folder.rights, folder.ownerUserId, folder.ownerGroupId, folder.storageId, link);
    } else {
        string link;
        //TODO: default group and storage
        id = g_db->FolderCreate(dirName, 0, 0, connectedUserId, 1, 1, link);
    }

    response.Push_S32(id);

    this->SendPacket(response);
}

void ServerClientProtocol::OnUserDeleteDirectory(const NetworkPacket &request) {
    FUNCTION_TRACK
    uint32_t folderId = 0;
    NetworkPacket response(request);
    request.Get_U32(0, folderId);

    DBUG("Removing directory %d", folderId);

    vector<Folder> list = g_db->FolderList(folderId);
    {



    }
    int32_t res = g_db->FolderRemove(folderId);

    response.Push_S32(res);
    this->SendPacket(response);
}

int32_t ServerClientProtocol::RemoveDirectory(uint32_t folderId) {
    //wpierw pliki
    vector<File> files = g_db->FileList(folderId);

    for (int i = 0; i < files.size(); i++) {
        DBUG("RemoveDirectory: Removing file %d link %s storage %d", files[i].id, files[i].link.c_str(), files[i].storageId);

        ServerStorageProtocol* protocol = ConnectionManager::getInstance().getStorageConnection(files[i].storageId);
        if (protocol->FileDelete(files[i].link) != 0) {
            DBUG("RemoveDirectory: Error on removing file %d link %s storage %d", files[i].id, files[i].link.c_str(), files[i].storageId);
            return 0;
        }
    }

    vector<Folder> folders = g_db->FolderList(folderId);
    for (int i = 0; i < folders.size(); i++) {
        DBUG("RemoveDirectory: Removing folder %d link %s storage %d", folders[i].id, folders[i].link.c_str(), folders[i].storageId);

        //rekurencyjne wywołanie
        RemoveDirectory(folders[i].id);

        ServerStorageProtocol* protocol = ConnectionManager::getInstance().getStorageConnection(folders[i].storageId);
        if (protocol->FolderDelete(folders[i].link) != 0) {
            DBUG("RemoveDirectory: Error on removing folder %d link %s storage %d", folders[i].id, folders[i].link.c_str(), folders[i].storageId);
            return 0;
        }
    }
    //vector<Folder>
    return 0;
}

int32_t ServerClientProtocol::RemoveDirectoryOnStorage(uint32_t folderId, uint32_t storageId) {
    //wpierw pliki
    vector<File> files = g_db->FileList(folderId);

    for (int i = 0; i < files.size(); i++) {
        DBUG("RemoveDirectory: Removing file %d link %s storage %d", files[i].id, files[i].link.c_str(), files[i].storageId);

        if (files[i].storageId == storageId) {
            ServerStorageProtocol* protocol = ConnectionManager::getInstance().getStorageConnection(files[i].storageId);
            if (protocol->FileDelete(files[i].link) != 0) {
                DBUG("RemoveDirectory: Error on removing file %d link %s storage %d", files[i].id, files[i].link.c_str(), files[i].storageId);
                return 0;
            }
        }
    }

    vector<Folder> folders = g_db->FolderList(folderId);
    for (int i = 0; i < folders.size(); i++) {
        DBUG("RemoveDirectory: Removing folder %d link %s storage %d", folders[i].id, folders[i].link.c_str(), folders[i].storageId);

        //rekurencyjne wywołanie
        RemoveDirectory(folders[i].id);
        if (folders[i].storageId == storageId) {
            ServerStorageProtocol* protocol = ConnectionManager::getInstance().getStorageConnection(folders[i].storageId);
            if (protocol->FolderDelete(folders[i].link) != 0) {
                DBUG("RemoveDirectory: Error on removing folder %d link %s storage %d", folders[i].id, folders[i].link.c_str(), folders[i].storageId);
                return 0;
            }
        }
    }
    //vector<Folder>
    return 0;
}

void ServerClientProtocol::OnUserRenameFile(const NetworkPacket &request) {
    FUNCTION_TRACK
}

void ServerClientProtocol::OnUserRenameDirectory(const NetworkPacket &request) {
    FUNCTION_TRACK
    uint32_t folderId = 0;
    NetworkPacket response(request);
    int32_t id;

    request.Get_U32(0, folderId);
    string dirName = _Get_String(request, 1);

    Folder folder = g_db->FolderGet(folderId);

    if (folder.id != 0) {
        int32_t status = g_db->FolderRename(folderId, dirName);
        response.Push_S32(status);
    } else {
        ERR("Rename directory that not exist id %d", folderId);
        response.Push_S32(-1);
    }

    this->SendPacket(response);
}
/*
void ServerClientProtocol::OnUserChmod(const NetworkPacket &request) {
    FUNCTION_TRACK
            //TODO: implement
}

void ServerClientProtocol::OnUserChown(const NetworkPacket &request) {
    FUNCTION_TRACK
            //TODO: implement
}
*/
void ServerClientProtocol::OnUserAddUser(const NetworkPacket &request) {
    FUNCTION_TRACK
    NetworkPacket response(request);

    string userName = _Get_String(request, 0);
    string password = _Get_String(request, 1);

    DBUG("Adding user %s", userName.c_str());

    response.Push_S32(g_db->UserAdd(userName, password));

    this->SendPacket(response);
}

void ServerClientProtocol::OnUserRemoveUser(const NetworkPacket &request) {
    FUNCTION_TRACK
    NetworkPacket response(request);

    uint32_t folderId;
    request.Get_U32(0, folderId);

    DBUG("Removing user %d", folderId);

    response.Push_S32(g_db->UserRemove(folderId));

    this->SendPacket(response);
}

void ServerClientProtocol::OnUserAddGroup(const NetworkPacket &request) {
    FUNCTION_TRACK
    NetworkPacket response(request);

    string name = _Get_String(request, 0);

    DBUG("Adding group %s", name.c_str());

    response.Push_S32(g_db->GroupAdd(name));

    this->SendPacket(response);
}

void ServerClientProtocol::OnUserRemoveGroup(const NetworkPacket &request) {
    FUNCTION_TRACK
    NetworkPacket response(request);

    uint32_t groupId;
    request.Get_U32(0, groupId);

    DBUG("Removing group %d", groupId);

    response.Push_S32(g_db->GroupRemove(groupId));

    this->SendPacket(response);
}

void ServerClientProtocol::OnUserAddStorage(const NetworkPacket &request) {
    FUNCTION_TRACK
    NetworkPacket response(request);

    string name = _Get_String(request, 0);
    string key = _Get_String(request, 1);

    int32_t status = g_db->StorageAdd(name, key);

    if (status == 0) {
        ERR("Failed to add storage %s id %d", name.c_str(), status);
    } else {
        DBUG("Adding storage %s id %d", name.c_str(), status);
    }

    response.Push_S32(status);

    this->SendPacket(response);
}

void ServerClientProtocol::OnUserRemoveStorage(const NetworkPacket &request) {
    FUNCTION_TRACK
    NetworkPacket response(request);

    uint32_t storageId;
    request.Get_U32(0, storageId);

    int32_t status = g_db->StorageRemove(storageId);

    DBUG("Removing storage %d status %d", storageId);
    response.Push_S32(status);

    this->SendPacket(response);
}

void ServerClientProtocol::OnUserChangeStorageKey(const NetworkPacket & request) {
    FUNCTION_TRACK
    string storage = _Get_String(request, 0);
    string key = _Get_String(request, 1);
    NetworkPacket response(request);

    response.Push_S32(g_db->StorageChangeKey(storage, key));

    this->SendPacket(response);

}

void ServerClientProtocol::OnUnShare(const NetworkPacket &request) {
    FUNCTION_TRACK
    boost::shared_ptr<PathInfo> path = g_db->GetInfo(request.GetString(0));
    if (NULL == path.get()) {
        RAISE_EXCEPTION(EException_WrongArgument, "Path is invalid: '" + request.GetString(0) + "'");
    }

    if (!path->IsExists())
    {
        RAISE_EXCEPTION(EException_InvalidPath, "Path not exists: '" + request.GetString(0) + "'");
    }

    if (path->IsDirectStorageChild())
    {
        RAISE_EXCEPTION(EException_InvalidOperation, "Can't unshare child of shared folder: '" + path->GetPath() + "'");
    }

    if ((path->GetStorageId() == 0) || (path->GetLink().empty()))
    {
        RAISE_EXCEPTION(EException_InvalidOperation, "Can't unshare pure virtual folder: '" + path->GetPath() + "'");
    }

    bool force = false;
    request.Get_Bool(1, force);

    uint32_t ret = force ? g_db->FolderRemove(path->GetId()) : g_db->FolderRemove(path->GetId(), path->GetStorageId());

    NetworkPacket response(request, CSNetworkLib::PACKET_CLIENT_UnShareResp);
    if (0 == ret)
    {
        ERR("Failed to unshare folder - %s", path->GetPath().c_str());
        response.Push_Bool(false);
    }
    else
    {
        INFO("Folder unshared - %s", path->GetPath().c_str());
        response.Push_Bool(true);
    }

    SendPacket(response);
}

void ServerClientProtocol::OnShare(const NetworkPacket &request) {
    FUNCTION_TRACK
    //get Storage ID
    uint32_t storageId = g_db->StorageGetId(request.GetString(2));
    if (0 == storageId) {
        RAISE_EXCEPTION(EException_WrongArgument, "Storage not exists: '" + request.GetString(2) + "'");
    }

    //Check path info
    boost::shared_ptr<PathInfo> path = g_db->GetInfo(request.GetString(0));
    if (NULL == path.get()) {
        RAISE_EXCEPTION(EException_WrongArgument, "Path is invalid: '" + request.GetString(0) + "'");
    }

    bool force = false;
    request.Get_Bool(3, force);
    //Check if exists in database..

    if (path->IsExists() && !force && !path->IsTerminated()) {
        if (path->GetType() == PATH_FOLDER) {
            RAISE_EXCEPTION(EException_FolderExits, "Folder on this path already exists, use FORCE flage to merge it");
        } else {
            RAISE_EXCEPTION(EException_FileExits, "File on this path already exists, use FORCE flage to replace it");
        }
    }

    if (path->IsTerminated() && !path->IsExists()) {
        RAISE_EXCEPTION(EException_FileExits, "Virtual folder '" + path->GetPath() + "' not exists");
    }

    if (!path->IsTerminated() && path->IsDirectStorageChild()) {
        RAISE_EXCEPTION(EException_InvalidOperation, "Can't share storage folder into direct child of folder: '" + path->GetParent()->GetPath() + "'");
    }

    //check storage...
    ServerStorageProtocol* protocol = ConnectionManager::getInstance().getStorageConnection(storageId);
    if (NULL == protocol) {
        RAISE_EXCEPTION(EException_StorageOffline, "Storage " + request.GetString(2) + " not connected, operation failed");
    }

    //get link info from storage...
    std::string glink, gname;
    EType ret = protocol->GetInfo(request.GetString(1), &glink, &gname);
    if (ETYPE_ERROR == ret) {
        RAISE_EXCEPTION(EException_WrongArgument, "Storage path '" + request.GetString(1) + "' is invalid");
    }
    if (ETYPE_NONE == ret) {
        RAISE_EXCEPTION(EException_InvalidPath, "Storage path '" + request.GetString(1) + "' not exists");
    }
    if (gname == ".") {
        RAISE_EXCEPTION(EException_InvalidPath, "Storage path '" + gname + "' have to end with folder or filename, not a '/', '\', '.'");
    }

    if (path->IsTerminated() && (gname.empty() || gname == "/" || gname == ":" || gname == "\\")) {
        RAISE_EXCEPTION(EException_InvalidPath, "Storage path '" + request.GetString(1) + "' can't be shared into / terminated virtual path, name not found");
    }

    if (path->IsTerminated()) {
        std::string old = path->GetPath() + gname;
        path = g_db->GetInfo(old);
        if (NULL == path.get()) {
            RAISE_EXCEPTION(EException_WrongArgument, "Path is invalid: '" + old + "'");
        }
        if (path->IsExists() && !force) {
            if (path->GetType() == PATH_FOLDER) {
                RAISE_EXCEPTION(EException_FolderExits, "Folder on this path already exists, use FORCE flage to merge it");
            } else {
                RAISE_EXCEPTION(EException_FileExits, "File on this path already exists, use FORCE flage to replace it");
            }
        }
        if (path->IsDirectStorageChild()) {
            RAISE_EXCEPTION(EException_InvalidOperation, "Can't share storage folder into direct child of folder: '" + path->GetParent()->GetPath() + "'");
        }
    }

    bool updated = false;
    if (path->IsExists()) {
        if ((ETYPE_FILE == ret) && (path->GetType() == PATH_FOLDER)) {
            //remove folder
            g_db->FolderRemove(path->GetId());
        } else if ((ETYPE_FOLDER == ret) && (path->GetType() == PATH_FILE)) {
            //remove file
            g_db->FileRemove(path->GetId());
        } else if ((ETYPE_FOLDER == ret) && (path->GetType() == PATH_FOLDER)) {
            //Update storage id & link, also creation date..
            g_db->FolderEdit(path->GetId(), path->GetName(), path->GetParent()->GetRights(), GetClientId(), path->GetParent()->GetGroup(), glink, storageId);
            updated = true;
        } else if ((ETYPE_FILE == ret) && (path->GetType() == PATH_FILE)) {
            //Update storage id & link, also creation date..
            g_db->FileEdit(path->GetId(), path->GetName(), path->GetParent()->GetRights(), GetClientId(), path->GetParent()->GetGroup(), glink, storageId);
            updated = true;
        }
    }
    int32_t newId = 0;
    if (!updated) {
        //insert new one...
        if (ETYPE_FILE == ret) {
            newId = g_db->FileCreate(path->GetParent()->GetId(), path->GetName(), path->GetParent()->GetRights(), GetClientId(), path->GetParent()->GetGroup(), 0, glink, storageId);
        } else
            if (ETYPE_FOLDER == ret) {
            newId = g_db->FolderCreate(path->GetName(), path->GetParent()->GetId(), path->GetParent()->GetRights(), GetClientId(), path->GetParent()->GetGroup(), storageId, glink, 0);
        } else
            newId = path->GetId();
    }
    if (ETYPE_FILE == ret) {
        protocol->GetInfo(glink);
    } else if (ETYPE_FOLDER == ret) {
        protocol->FolderSync(glink);
    }
    NetworkPacket response(request, CSNetworkLib::PACKET_CLIENT_ShareResp);
    path = g_db->GetInfo(path->GetPath());
    if (NULL == path.get()) {
        ERR("Path is empty");
        response.Push_Bool(false);
    } else if (!path->IsExists()) {
        ERR("Path not exists - %s", path->GetPath().c_str());
        response.Push_Bool(false);
    } else if (path->GetId() != newId) {
        ERR("Path got diffrent id: %d insted of %d", path->GetId(), newId);
        response.Push_Bool(false);
    } else if (0 == path->GetSyncTime()) {
        ERR("Sync time is 0");
        response.Push_Bool(false);
    } else if (path->GetLink() != glink) {
        ERR("Link changed from %s to %s", glink.c_str(), path->GetLink().c_str());
        response.Push_Bool(false);
    } else if (path->GetStorageId() != storageId) {
        ERR("Storage id changed from %d to %d", storageId, path->GetStorageId());
        response.Push_Bool(false);
    } else
        response.Push_Bool(true);
    SendPacket(response);
}

void ServerClientProtocol::OnFileExists(const NetworkPacket & request)
{
    FUNCTION_TRACK
    boost::shared_ptr<PathInfo> path = g_db->GetInfo(request.GetString(0));
    if (NULL == path.get()) {
        RAISE_EXCEPTION(EException_WrongArgument, "Path is invalid: '" + request.GetString(0) + "'");
    }

    if (path->IsTerminated())
    {
        RAISE_EXCEPTION(EException_InvalidPath, "Folder type path: '" + request.GetString(0) + "'");
    }

    if (path->IsExists() && path->GetType() == PATH_FOLDER)
    {
        RAISE_EXCEPTION(EException_FolderExits, "Folder found on '" + request.GetString(0) + "'");
    }

    NetworkPacket response(request, CSNetworkLib::PACKET_CLIENT_FileExistsResp);
    response.Push_Bool(path->IsExists());
    SendPacket(response);
}

void ServerClientProtocol::OnFileDelete(const NetworkPacket &request) {
    FUNCTION_TRACK

    boost::shared_ptr<PathInfo> path = g_db->GetInfo(request.GetString(0));
    if (NULL == path.get()) {
        RAISE_EXCEPTION(EException_WrongArgument, "Path is invalid: '" + request.GetString(0) + "'");
    }
    if (path->IsTerminated())
    {
        RAISE_EXCEPTION(EException_InvalidPath, "Folder type path: '" + request.GetString(0) + "'");
    }

    if (path->IsExists() && path->GetType() == PATH_FOLDER)
    {
        RAISE_EXCEPTION(EException_FolderExits, "Folder found on '" + request.GetString(0) + "'");
    }

    if (!path->IsExists())
    {
        NetworkPacket response(request, PACKET_CLIENT_FileDeleteResp);
        response.Push_Bool(true);
        this->SendPacket(response);
    }

    if (!path->IsDirectStorageChild())
    {
        RAISE_EXCEPTION(EException_InvalidOperation, "File '" + request.GetString(0) + "' is not owned by parent folder, use UnShare to remove it");
    }

    ServerStorageProtocol* protocol = ConnectionManager::getInstance().getStorageConnection(path->GetStorageId());
    if (NULL == protocol) {
        RAISE_EXCEPTION(EException_StorageOffline, "Storage not connected, operation failed");
    }

    NetworkPacket response(request, PACKET_CLIENT_FileDeleteResp);
    response.Push_Bool(0 == protocol->FileDelete(path->GetLink()));
    this->SendPacket(response);
}

void ServerClientProtocol::OnFileOpen(const NetworkPacket &request) {
    FUNCTION_TRACK
    boost::shared_ptr<PathInfo> path = g_db->GetInfo(request.GetString(0));
    if (NULL == path.get()) {
        RAISE_EXCEPTION(EException_WrongArgument, "Path is invalid: '" + request.GetString(0) + "'");
    }
    if (path->IsTerminated())
    {
        RAISE_EXCEPTION(EException_InvalidPath, "Folder type path: '" + request.GetString(0) + "'");
    }

    if (path->IsExists() && path->GetType() == PATH_FOLDER)
    {
        RAISE_EXCEPTION(EException_FolderExits, "Folder found on '" + request.GetString(0) + "'");
    }

    if ((request.GetString(1).find("r") == std::string::npos) && (request.GetString(1).find("a") == std::string::npos) && (request.GetString(1).find("w") == std::string::npos))
    {
        RAISE_EXCEPTION(EException_WrongArgument, "Allowed mode is only: 'r', 'w', 'a' and combinations of them");
    }

    if (!path->IsExists() && (request.GetString(1).find("a") == std::string::npos) && (request.GetString(1).find("w") == std::string::npos))
    {
        RAISE_EXCEPTION(EException_FileNotExits, "Can't open non exist file for reading");
    }

    if (!path->IsExists() && path->GetParent()->IsVirtual())
    {
        RAISE_EXCEPTION(EException_InvalidOperation, "Can't create file under pure virtual folder : '" + request.GetString(0) + "'");
    }

    uint32_t handle = 0;

    if (path->IsExists())
    {
        //use file settings...
        ServerStorageProtocol* protocol = ConnectionManager::getInstance().getStorageConnection(path->GetStorageId());
        if (NULL == protocol) {
            RAISE_EXCEPTION(EException_StorageOffline, "Storage not connected, operation failed");
        }
        uint32_t hs = protocol->FileOpen(path->GetLink(), request.GetString(1));
        if (0 != hs)
        {
            handle = GetNextHandle();
            ConnectionManager::getInstance().PutHandle(handle, hs, this, protocol);
        }
    }
    else
    {
        ServerStorageProtocol* protocol = ConnectionManager::getInstance().getStorageConnection(path->GetParent()->GetStorageId());
        if (NULL == protocol) {
            RAISE_EXCEPTION(EException_StorageOffline, "Storage not connected, operation failed");
        }
        uint32_t hs = protocol->FileOpen(path->GetParent()->GetLink(), path->GetName(), request.GetString(1));
        if (0 != hs)
        {
            handle = GetNextHandle();
            ConnectionManager::getInstance().PutHandle(handle, hs, this, protocol);
        }
    }

    NetworkPacket response(request, CSNetworkLib::PACKET_CLIENT_FileOpenResp);
    response.Push_U32(handle);
    this->SendPacket(response);
}

void ServerClientProtocol::OnFileClose(const NetworkPacket &request) {
    FUNCTION_TRACK
    uint32_t handle = 0;
    request.Get_U32(0, handle);
    if (0 == handle)
    {
        RAISE_EXCEPTION(EException_WrongArgument, "Handle is 0");
    }

    boost::shared_ptr<ConnectionManager::SStorageHandle> h = ConnectionManager::getInstance().GetHandle(handle, this);
    if (NULL == h.get())
    {
        RAISE_EXCEPTION(EException_InvalidOperation, "Handle is invalid, already closed or forced to by closed by storage.");
    }

    bool ret = h->storage->FileClose(h->handle);
    if (ret)
    {
        ConnectionManager::getInstance().RemoveHandle(handle, this);
    }
    NetworkPacket response(request, CSNetworkLib::PACKET_CLIENT_FileCloseResp);
    response.Push_Bool(ret);
    this->SendPacket(response);
}

void ServerClientProtocol::OnFileInfo(const NetworkPacket & request)
{
    FUNCTION_TRACK
    uint32_t handle = 0;
    request.Get_U32(0, handle);
    if (0 == handle)
    {
        RAISE_EXCEPTION(EException_WrongArgument, "Handle is 0");
    }

    uint8_t mode = 0;
    request.Get_U8(1, mode);
    if (mode>2)
    {
        RAISE_EXCEPTION(EException_WrongArgument, "Mode not valid, only 0 - size, 1 - last modyfication time, 2 - position is valid");
    }

    boost::shared_ptr<ConnectionManager::SStorageHandle> h = ConnectionManager::getInstance().GetHandle(handle, this);
    if (NULL == h.get())
    {
        RAISE_EXCEPTION(EException_InvalidOperation, "Handle is invalid, already closed or forced to by closed by storage.");
    }
    uint64_t t = 0;
    if (!h->storage->FileInfo(h->handle, mode, t))
    {
        RAISE_EXCEPTION(EException_OperationFailed, "Operation on file failed by storage, storage can be not synced");
    }
    NetworkPacket response(request, CSNetworkLib::PACKET_CLIENT_FileInfoResp);
    response.Push_U64(t);
    this->SendPacket(response);
}

void ServerClientProtocol::OnFileSeek(const NetworkPacket & request)
{
    FUNCTION_TRACK
    uint32_t handle = 0;
    request.Get_U32(0, handle);
    if (0 == handle)
    {
        RAISE_EXCEPTION(EException_WrongArgument, "Handle is 0");
    }

    int64_t position = 0;
    request.Get_S64(1, position);

    boost::shared_ptr<ConnectionManager::SStorageHandle> h = ConnectionManager::getInstance().GetHandle(handle, this);
    if (NULL == h.get())
    {
        RAISE_EXCEPTION(EException_InvalidOperation, "Handle is invalid, already closed or forced to by closed by storage.");
    }

    NetworkPacket response(request, CSNetworkLib::PACKET_CLIENT_FileSeekResp);
    response.Push_Bool(h->storage->FileSeek(h->handle, position));
    this->SendPacket(response);
}

void ServerClientProtocol::OnFileEof(const NetworkPacket & request)
{
    FUNCTION_TRACK
    uint32_t handle = 0;
    request.Get_U32(0, handle);
    if (0 == handle)
    {
        RAISE_EXCEPTION(EException_WrongArgument, "Handle is 0");
    }

    boost::shared_ptr<ConnectionManager::SStorageHandle> h = ConnectionManager::getInstance().GetHandle(handle, this);
    if (NULL == h.get())
    {
        RAISE_EXCEPTION(EException_InvalidOperation, "Handle is invalid, already closed or forced to by closed by storage.");
    }

    NetworkPacket response(request, CSNetworkLib::PACKET_CLIENT_FileEofResp);
    response.Push_Bool(h->storage->FileEof(h->handle));
    this->SendPacket(response);
}

void ServerClientProtocol::OnFileWrite(const NetworkPacket & request)
{
    FUNCTION_TRACK
    uint32_t handle = 0;
    request.Get_U32(0, handle);
    if (0 == handle)
    {
        RAISE_EXCEPTION(EException_WrongArgument, "Handle is 0");
    }

    boost::shared_ptr<ConnectionManager::SStorageHandle> h = ConnectionManager::getInstance().GetHandle(handle, this);
    if (NULL == h.get())
    {
        RAISE_EXCEPTION(EException_InvalidOperation, "Handle is invalid, already closed or forced to by closed by storage.");
    }

    NetworkPacket response(request, CSNetworkLib::PACKET_CLIENT_FileWriteResp);
    int32_t ret = h->storage->FileWrite(h->handle, request.GetItemSize(1), request.Get_DataPtr(1));
    response.Push_U32(ret>0?ret:0);
    this->SendPacket(response);
}

void ServerClientProtocol::OnFileRead(const NetworkPacket & request)
{
    FUNCTION_TRACK
    uint32_t handle = 0, size = 0;
    request.Get_U32(0, handle);
    request.Get_U32(1, size);
    if (0 == handle)
    {
        RAISE_EXCEPTION(EException_WrongArgument, "Handle is 0");
    } else if (0 == size)
    {
        RAISE_EXCEPTION(EException_WrongArgument, "Size is 0");
    }

    boost::shared_ptr<ConnectionManager::SStorageHandle> h = ConnectionManager::getInstance().GetHandle(handle, this);
    if (NULL == h.get())
    {
        RAISE_EXCEPTION(EException_InvalidOperation, "Handle is invalid, already closed or forced to by closed by storage.");
    }

    NetworkPacket response(request, CSNetworkLib::PACKET_CLIENT_FileReadResp);
    uint8_t *data = new uint8_t[size+1];
    int32_t ret = h->storage->FileRead(h->handle, size, data);
    if (ret>0)
    {
        response.Push_Data(data, ret);
    }
    delete []data;
    this->SendPacket(response);
}

void ServerClientProtocol::OnUserGetUsersList(const NetworkPacket & request) {
    FUNCTION_TRACK
    NetworkPacket response(request);

    vector<User> users = g_db->UsersList();

    response.Push_S32(users.size());

    for (int i = 0; i < users.size(); i++) {
        _Push_User(response, users[i]);
    }

    this->SendPacket(response);
}

void ServerClientProtocol::OnUserGetGroupsList(const NetworkPacket & request) {
    FUNCTION_TRACK
    NetworkPacket response(request);

    vector<Group> groups = g_db->GroupList();

    response.Push_S32(groups.size());

    for (int i = 0; i < groups.size(); i++) {
        _Push_Group(response, groups[i]);
    }

    this->SendPacket(response);
}

void ServerClientProtocol::OnUserGetStoragesList(const NetworkPacket & request) {
    FUNCTION_TRACK
    NetworkPacket response(request);

    vector<Storage> storages = g_db->StorageList();

    response.Push_S32(storages.size());

    for (int i = 0; i < storages.size(); i++) {
        _Push_Storage(response, storages[i]);
    }

    this->SendPacket(response);
}

ServerClientProtocol::ServerClientProtocol(socket_ptr s) : BaseServerProtocol(s) {
    FUNCTION_TRACK
    this->connectedUserId = 0;
    m_handleId = 0;
    AddHandler<ServerClientProtocol > ((int32_t) CSNetworkLib::PACKET_CLIENT_AutorizationReq, & ServerClientProtocol::OnUserAuthorization);
}

ServerClientProtocol::~ServerClientProtocol() {
    FUNCTION_TRACK
    ConnectionManager::getInstance().CloseHandlesClient(this);
    ConnectionManager::getInstance().RemoveClient(connectedUserId, this);
    connectedUserId = 0;
}
